/** @file

  Copyright (C) 2022 - 2023, Phytium Technology Co., Ltd. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/ArmPlatformLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/IoLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/ArmSmcLib.h>

#define FT_I2C1_BASE		0x28007000

#define IC_CON				0x00
#define IC_TAR				0x04	//target address
#define IC_SAR				0x08	//slave address
#define IC_DATA_CMD			0x10	//Rx/Tx data buffer, cmd reg
#define IC_FS_SCL_HCNT		0x1c
#define	IC_FS_SCL_LCNT		0x20
#define IC_INTR_STAT		0x2c
#define IC_INTR_MASK		0x30
#define IC_RAW_INTR_STAT	0x34
#define IC_RX_TL			0x38
#define IC_TX_TL			0x3c
#define IC_CLR_TX_ABORT		0x54
#define IC_CLR_STOP_DET		0x60
#define IC_ENABLE			0x6c
#define IC_STATUS			0x70
#define IC_SDA_HOLD			0x7c

STATIC
inline
UINT32
i2c_read_reg(
  IN UINTN reg
  )
{
  UINT32  Value;
  Value = MmioRead32(FT_I2C1_BASE +reg);
  return Value;
}

STATIC
inline
VOID
i2c_write_reg(
  UINTN reg, 
  UINT32 value
  )
{
  UINTN reg_addr ;
  reg_addr = FT_I2C1_BASE + reg;
  MmioWrite32(reg_addr, value);
}

STATIC
VOID
ft_i2c_init(
  VOID
  )
{
  UINT32 hcnt = 166*100/1000;
  UINT32 lcnt = 166*1300/1000;
  i2c_write_reg(IC_ENABLE,		0x0);
  i2c_write_reg(IC_FS_SCL_HCNT,	hcnt);  //快速模式下高电平持续时间计数值
  i2c_write_reg(IC_FS_SCL_LCNT,	lcnt);  //快速模式下低电平持续时间计数值
  i2c_write_reg(IC_CON,			0x65);
  i2c_write_reg(IC_SDA_HOLD,	0x30);
  i2c_write_reg(IC_ENABLE,		0x1);
}

STATIC
VOID
ft_i2c_xfer_init(
  UINT8 target_addr, 
  UINT64 addr
  )
{
  DEBUG((EFI_D_INFO,"target_addr: 0x%x\n", target_addr));
  DEBUG((EFI_D_INFO,"write_addr: 0x%llx\n", addr));
  while((i2c_read_reg(IC_STATUS) & 0x20) || !(i2c_read_reg(IC_STATUS) & 0x4));	//等待[5]=0,master idle [2]=1,transmit FIFO empty
  i2c_write_reg(IC_ENABLE,		0x0);
  i2c_write_reg(IC_TAR,			target_addr);
  i2c_write_reg(IC_ENABLE,		0x1);

//写40bit地址
  i2c_write_reg(IC_DATA_CMD,		(addr>>32) & 0xff);		//[39:32]
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);					//等待[1]=1, transmit FIFO 不满
  i2c_write_reg(IC_DATA_CMD,		(addr>>24) & 0xff);		//[31:24]
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,		(addr>>16) & 0xff);		//[23:16]
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,		(addr>>8) & 0xff);		//[15:8]
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,		(addr>>0) & 0xff);		//[7:0]

}

STATIC
VOID
ft_i2c_xfer_finish(
  VOID
  )
{
	while(1){
		if(i2c_read_reg(IC_RAW_INTR_STAT) & 0x200){		//待[9]=1,stop_det
			i2c_read_reg(IC_CLR_STOP_DET);				//读reg,clear stop det
			break;
		}
	}
	while((i2c_read_reg(IC_STATUS) & 0x20) || !(i2c_read_reg(IC_STATUS) & 0x4));		//等待[5]=0,master idle [2]=1,transmit FIFO empty
	while(i2c_read_reg(IC_STATUS) & 0x8)		//待[3]=0, 如果[3]=1, 读IC_DATA_CMD, flush the i2c RX FIFO
		i2c_read_reg(IC_DATA_CMD);
	
}

UINTN
read_se_state(
  VOID
  )
{
	int cnt = 0, i = 0, read_value = 0, read_temp = 0;

  MmioWrite32(0x28180200,0xee884444);//I2C1
    //printf("1. i2c init \n");
  ft_i2c_init();
  //printf("2. xfer init \n");
  ft_i2c_xfer_init(0x18, (UINT64)0x7002f00c);
  //printf("3. read \n");
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);		//等待[1]=1, transmit FIFO 不满
  i2c_write_reg(IC_DATA_CMD,			0x1);			//send d_width, 0 for 4 byte, 1 for 8 byte

  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);

  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			0x1<<8);
  while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
  i2c_write_reg(IC_DATA_CMD,			(0x1<<8) | (0x1<<9));
	for(i=0; i<8; i++){
		cnt = 0;
		while(!(i2c_read_reg(IC_STATUS) & 0x8)){		//待[3]=1, Receive FIFO 不空
			cnt++;
			if(cnt >= 5000)
				break;
		}
		if(cnt >= 5000){
			DEBUG((EFI_D_INFO,"read error!\n"));
			return -1;
		}	
		read_temp = i2c_read_reg(IC_DATA_CMD);
		read_value |= (read_temp << ((7-i)*8));
	}
	DEBUG((EFI_D_INFO,"read value: 0x%x\n", read_value));

    //printf("4. xfer finish \n");
  ft_i2c_xfer_finish();

  return read_value;
}

VOID
send_se_ctr(
  UINT32 cmd
  )
{
    UINT64    write_value = cmd;

    MmioWrite32(0x28180200,0xee884444);//I2C1
    //p_printf("1. i2c init \n");
	ft_i2c_init();
    //p_printf("2. xfer init \n");
	ft_i2c_xfer_init(0x18, (UINT64)0x70021024);

    write_value = write_value | (0x1<<8);
    //p_printf("3. write \n");
 	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);		//等待[1]=1, transmit FIFO 不满
	i2c_write_reg(IC_DATA_CMD,			0x0);			//send d_width, 0 for 4 byte, 1 for 8 byte
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>24)&0xff) | (0x0<<8));			//[31:24]
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>16)&0xff) | (0x0<<8));			//[23:16]
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>8)&0xff) | (0x0<<8));			//[15:8]
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>0)&0xff) | (0x0<<8) | (0x1<<9));	//[7:0] , 增加发送一个stop信号   
	//p_printf("4. xfer finish \n");
	ft_i2c_xfer_finish();
    DEBUG((EFI_D_INFO,"se write cmd finish: 0x%x\n", write_value));

    //p_printf("1. i2c init \n");
	ft_i2c_init();
    //p_printf("2. xfer init \n");
	ft_i2c_xfer_init(0x18, (UINT64)0x7002f010);

    write_value = write_value | (0x1<<8);
    //p_printf("3. write \n");
 	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);		//等待[1]=1, transmit FIFO 不满
	i2c_write_reg(IC_DATA_CMD,			0x0);			//send d_width, 0 for 4 byte, 1 for 8 byte
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>24)&0xff) | (0x0<<8));			//[31:24]
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>16)&0xff) | (0x0<<8));			//[23:16]
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>8)&0xff) | (0x0<<8));			//[15:8]
	while(((i2c_read_reg(IC_STATUS)>>1)&0x1) != 1);
	i2c_write_reg(IC_DATA_CMD,			((write_value>>0)&0xff) | (0x0<<8) | (0x1<<9));	//[7:0] , 增加发送一个stop信号
	//p_printf("4. xfer finish \n");
	ft_i2c_xfer_finish();
    DEBUG((EFI_D_INFO,"se cmd flush: 0x%x\n", write_value));
}
